/* uMMC.h

  uMMC Interface Library

  A library to communicate with the uMMC Data Storage Module
  from Rogue Robotics (http://www.roguerobotics.com/).
  Requires latest firmware (i.e. > 102.08.b001)
  See http://www.roguerobotics.com/faq/update_firmware for updating firmware.

  Written by Brett Hagman

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Version Modified By Date     Comments
------- ----------- -------- --------
0001    B Hagman    09/06/21 Initial coding

*************************************************/

#ifndef _uMMC_h
#define _uMMC_h

#include <stdint.h>
#include <Print.h>

/*************************************************
* Public Constants
*************************************************/
#define UMMC_DEFAULT_PROMPT                   0x3E

#define ERROR_BUFFER_OVERRUN                  0x02
#define ERROR_NO_FREE_FILES                   0x03
#define ERROR_UNRECOGNIZED_COMMAND            0x04
#define ERROR_CARD_INITIALIZATION_ERROR       0x05
#define ERROR_FORMATTING_ERROR                0x06
#define ERROR_EOF                             0x07
#define ERROR_CARD_NOT_INSERTED               0x08
#define ERROR_MMC_RESET_FAIL                  0x09
#define ERROR_CARD_WRITE_PROTECTED            0x0a
#define ERROR_INVALID_HANDLE                  0xf6
#define ERROR_OPEN_PATH_INVALID               0xf5
#define ERROR_FILE_ALREADY_EXISTS             0xf4
#define ERROR_DE_CREATION_FAILURE             0xf3
#define ERROR_FILE_DOES_NOT_EXIST             0xf2
#define ERROR_OPEN_HANDLE_IN_USE              0xf1
#define ERROR_OPEN_NO_FREE_HANDLES            0xf0
#define ERROR_FAT_FAILURE                     0xef
#define ERROR_SEEK_NOT_OPEN                   0xee
#define ERROR_OPEN_MODE_INVALID               0xed
#define ERROR_READ_IMPROPER_MODE              0xec
#define ERROR_FILE_NOT_OPEN                   0xeb
#define ERROR_NO_FREE_SPACE                   0xea
#define ERROR_WRITE_IMPROPER_MODE             0xe9
#define ERROR_WRITE_FAILURE                   0xe8
#define ERROR_NOT_A_FILE                      0xe7
#define ERROR_OPEN_READONLY_FILE              0xe6
#define ERROR_NOT_A_DIR                       0xe5

#define ERROR_NOT_SUPPORTED                   0xff


/*************************************************
* Class
*************************************************/

class uMMC : public Print
{
  public:
    // enums
    enum open_mode {OPEN_READ = 1, OPEN_WRITE = 2, OPEN_RW = 3, OPEN_APPEND = 4};

    // properties
    uint8_t LastErrorCode;

    // methods

    // constructor
    uMMC(int8_t (*_af)(void), int16_t (*_pf)(void), int16_t (*_rf)(void), void (*_wf)(uint8_t));

    int8_t sync(void);

    int8_t getfreehandle(void);
    int8_t open(const char *filename);
    int8_t open(const char *filename, open_mode mode);
    int8_t open(int8_t handle, const char *filename);
    int8_t open(int8_t handle, const char *filename, open_mode mode);
//    int8_t open(int8_t handle, const prog_char *filename);
//    int8_t open(int8_t handle, const prog_char *filename, uint8_t mode);

    int8_t opendir(const char *dirname);
    int32_t filecount(const char *filemask);
    int32_t filecount(const char *dirname, const char *filemask);
    int8_t readdir(char *filename, const char *filemask);

    // delete/remove a file/directory (directory must be empty)
//    int8_t remove(const char *filename);

    // rename a file/directory
//    int8_t rename(const char *oldname, const char *newname);

    // read single byte (-1 if no data)
    int16_t readbyte(int8_t handle);

    // read exactly count bytes into buffer
    int16_t read(int8_t handle, uint16_t count, char *buffer);

    // read up to maxlength characters into tostr
    int16_t readln(int8_t handle, uint8_t maxlength, char *tostr);

//    int16_t readprep(int8_t handle, uint16_t bytestoread);
    // you can use the SerialComm commands to read data
    // read what you want, then flush the serial interface

    // we will need to set up the write time-out to make this work properly (done in sync())
    // then you can use the Print functions to print to the file
    int8_t writeln(int8_t handle, const char *data);
    void writeln_prep(int8_t handle);
    int8_t writeln_finish(void);

    // write exactly count bytes to file
    int8_t write(int8_t handle, uint16_t count, const char *data);

    uint32_t getfilesize(int8_t handle); // get using "I fh" command
//    uint32_t getfilesize(const char *filename); // get using "L filename"

    uint32_t getfileposition(int8_t handle);
    
    int8_t seek(int8_t handle, uint32_t newposition);
    int8_t seektoend(int8_t handle);

//    void settime(uint32_t date, uint32_t time);
//    void settime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);

    void close(int8_t handle);
    void closeall(void);
//    int8_t changesetting(char setting, const char *value);
    int8_t changesetting(char setting, uint8_t value);
    int16_t getsetting(char setting);

    inline int16_t version(void) { return _fwversion; }

    void write(uint8_t);  // needed for Print

  private:
    // It would be nice to use some polymorphism here to talk to our SerialComm object (be it HardwareSerial, SoftSerial, NewSoftSerial, etc...)
    // but since there is no base class for the SerialComm objects (yet), we can't do that.
    // This is ugly, but maybe someone will define the SerialComm base class
    // SerialComm *SerialInterface;
    // usage: SerialInterface->read()
    //        SerialInterface->write()
    //        SerialInterface->available()
    // For now, we will have to use function pointers (clunky, yes)

    // properties
    // pointer to serial "available" function: returns number of bytes available, or 0 if none
    int8_t (*_availablef)(void);
    // pointer to serial "peek" function: returns the current byte at the top of the serial queue, -1 if no data
    int16_t (*_peekf)(void);
    // pointer to serial "read" function: dequeues and returns the next byte from the serial queue, -1 if no data
    int16_t (*_readf)(void);
    // pointer to serial "write" function: writes provided byte to serial
    void (*_writef)(uint8_t);
    uint8_t _promptchar;
    int16_t _fwversion;

    // methods
    uint32_t _get_filestats(int8_t handle, uint8_t valuetoget);
    int16_t _get_version(void);
    int8_t _get_response(void);
    void _flush(void);

    int8_t _read_blocked(void);
    int32_t _getnumber(uint8_t base);

};

#define OPEN_READ                             uMMC::OPEN_READ
#define OPEN_WRITE                            uMMC::OPEN_WRITE
#define OPEN_RW                               uMMC::OPEN_RW
#define OPEN_APPEND                           uMMC::OPEN_APPEND

#endif
